<?php
namespace Cli\Controller;

use \Think\Controller;
use Think\Log;

/**
 * socket服务器
 * @modify trainyao 2016-02-03 创建
 */
class SocketController extends Controller {
    private $tickClientIds = [];
    private $webSocketServer;
    private $webSocketCheckRoomServer;
    protected function _initialize() {
        if (!IS_CLI) {
            exit();
        }
    }

    public function start() {
        $webSocketInstance = new \swoole_websocket_server(C('SOCKET_SERVER_IP'), C('SOCKET_SERVER_PORT'));

        $webSocketInstance->on('WorkerStart', function ($server, $worker_id) {
            if (!$worker_id >= $server->setting['worker_num'] && !$server->inited) {
                // 初始化redisInstance
                if (empty($server->redisInstance)) {
                    $server->redisInstance = new \Redis();
                    $connectResult = $server->redisInstance->connect(C('REDIS_SERVERIP'), C('REDIS_SERVERPORT'));
                    if ($connectResult) {
                        $server->redisConnect = true;
                        $server->redisInstance->auth(C('REDIS_AUTH_PASSWORD'));
                        $server->redisInstance->select(C('REDIS_ROOM_DB'));
                    }
                }

                // 初始化task
                $server->tick(10000, function() use ($server) {
                    // 查redis数据
                    $data = array();
                    $dataValue = array();
                    if ($server->redisConnect) {
                        foreach ($server->hotels as $hotelId => $clientIds) {
                            if(!empty($clientIds)) {
                                $data = $server->redisInstance->keys('meizhu:*:' . $hotelId . '_*');
                                if (!empty($data)) {
                                    foreach ($data as $redisKey) {
                                        $dataValue = $server->redisInstance->hGetAll($redisKey);
                                        // 发送消息
                                        $dataArray = array(
                                            'clients' => $clientIds,
                                            'data' => $dataValue['value'],
                                        );
                                        $server->task($dataArray);
                                        // 使消息失效
                                        $server->redisInstance->EXPIREAT($redisKey, 0);
                                    }
                                }
                            }
                        }
                    }
                });
                $server->hotels = array();
                $server->connectorHotel = array();
                $server->inited = true;
            }
        });

        $webSocketInstance->on('message', function ($server, $frame) {
            $clientId = $frame->fd;
            $msg = $frame->data;

            $msg = json_decode($msg, true);
            if (in_array($msg['cmd'], array('set', 'close'))) {
                switch ($msg['cmd']) {
                    case 'set':
                        $hotelId = $msg['hotel'];
                        // 保存客栈用户关系
                        if (empty($server->hotels[$hotelId])) {
                            $server->hotels[$hotelId] = array(
                                $clientId
                            );
                        } else {
                            if (!in_array($clientId, $server->hotels[$hotelId])) {
                                array_push($server->hotels[$hotelId], $clientId);
                            }
                        }
                        // 保存用户客栈更新
                        $server->connectorHotel[$clientId] = array(
                            'hotel' => $hotelId,
                        );
                        $server->push($clientId, json_encode(array(
                            'httpstatus' => 200,
                            'status' => 200,
                            'info' => '',
                            'data' => '',
                        )));
                        break;
                }
            }
        });

        //监听WebSocket连接关闭事件
        $webSocketInstance->on('close', function ($server, $clientId, $from_id) {
            $hotelId = $server->connectorHotel[$clientId]['hotel'];
            $key = array_search($clientId, $server->hotels[$hotelId]);
            if ($key !== false) {
                unset($server->hotels[$hotelId][$key]);
                if (empty($server->hotels[$hotelId])) {
                    $server->hotels[$hotelId] = array();
                }
            }
            unset($server->connectorHotel[$clientId]);
        });

        $webSocketInstance->on('task', function ($server, $task_id, $from_id, $data) {
            foreach ($data['clients'] as $clientId) {
                $server->push($clientId, json_encode(array(
                    'httpstatus' => 200,
                    'status' => 200,
                    'info' => $data['data'],
                    'data' => '',
                )));
            }
        });

        $webSocketInstance->on('finish', function ($server) {
        });

        $webSocketInstance->set(array(
            'task_worker_num' => 4,
            'worker_num'      => 1,
            'max_request'     => 5000,
            'daemonize' => 1,
        ));
        $webSocketInstance->start();
    }

    public function stop() {
    }

    public function startNewsPush() {
        $this->webSocketServer = $webSocketInstance = new \swoole_websocket_server(C('SOCKET_SERVER_NEWS_IP'), C('SOCKET_SERVER_NEWS_PORT'));
        $clientId=[];
        $hotelIds=[];
        $connectorHotels = [];
        $webSocketInstance->on('WorkerStart', function ($server, $worker_id) {
            if (!$worker_id >= $server->setting['worker_num'] && !$server->inited) {
                // 初始化redisInstance
                if (empty($this->redisUtil)) {
                    $this->redisUtil = new \Org\Util\RedisHelper(null, null, 'meizhu');
                    $this->redisUtil->db(C('REDIS_ORDER_PUSH_DB'))->table('pushUser');
                }

                // 初始化task  点击知道了  消息同步
                $server->tick(3000, function() use ($server) {

                    $clientId = $this->tickClientIds;
                    // 查redis数据 获取pushUser表数据
                    $userIds = $this->redisUtil->redis->sMembers('userIds');
                    if (!empty($userIds) && !empty($clientId)) {

                        foreach ($clientId as $client => $userId) {

                            if (in_array($userId, $userIds)) {
                                $server->push($client, json_encode(array(
                                    'httpstatus' => 200,
                                    'status' => 200,
                                    'type' => 3,
                                )));
                                // 使消息失效
                                $this->redisUtil->redis->sRem('userIds', $userId);
                            }
                        }
                        unset($userId);
                    }
                });
                $server->inited = true;
            }
        });

        $webSocketInstance->on('message', function ($server, $frame) {
            global $clientId;
            global $hotelIds;
            global $connectorHotels;
            $msg = $frame->data;
            $msg = json_decode($msg, true);

            // 定时推送 
            $this->tickClientIds[$frame->fd] = $msg['adminId'];

            $HotelRoomAuthorityModel = D('HotelRoomAuthority');
            $adminHotelModel = D('AdminHotel');
            // 获取用户管理客栈id 并且客栈开启状态
            $adminHotelInfo = $adminHotelModel->getInfoByUserLeftHotelEntity($msg['adminId']);
            $userHotelIds = array_column($adminHotelInfo, 'hotelentity_id');
            // 保存客栈用户关系
            if (empty($hotelIds)) {
                $hotelIds[$msg['adminId']] = array(
                    $frame->fd =>  $userHotelIds,
                );
            } else {
                if (isset($hotelIds[$msg['adminId']])) {
                    $hotelIds[$msg['adminId']][$frame->fd] = $userHotelIds;
                } else {
                    $hotelIds[$msg['adminId']] = array(
                        $frame->fd =>  $userHotelIds,
                    );
                }
            }

            $clientId[$frame->fd] = $msg['adminId'];
            foreach ($hotelIds as $adminId => $admin) {
                foreach ($admin as $client => $hotels) {
                    if (in_array($msg['hotel'], $hotels)) {
                        // 微预定 预定时推送
                        if (isset($msg['roomId'])) {
                            // 检查用户是否是客栈拥有者
                            $adminInfo = $adminHotelModel->getInfosByAdmin($adminId, 1, $msg['hotel']);
                            $isAdminUser = empty($adminInfo) ? false : true;
                            // 管理员对该房间的权限  (掌柜直接推送)
                            $HotelRoomAuthorityInfo = $HotelRoomAuthorityModel->getInfoByUserRoom($adminId, $msg['roomId']);
                            if (!empty($HotelRoomAuthorityInfo) || $isAdminUser) {
                                $server->push($client, json_encode(array(
                                    'httpstatus' => 200,
                                    'status' => 200,
                                    'type' => 2,
                                )));
                            }
                        } else {
                            // 房态刷新时推送
                            $server->push($client, json_encode(array(
                                'httpstatus' => 200,
                                'status' => 200,
                                'type' => 2,
                            )));
                        }
                    }
                }
            }

            $server->task($msg);
        });

        //监听WebSocket连接关闭事件
        $webSocketInstance->on('close', function ($server, $client, $from_id) {
            global $clientId;
            global $hotelIds;
            global $connectorHotels;

            $adminId = $clientId[$client];
            unset($hotelIds[$adminId][$client]);
            if (empty($hotelIds[$adminId])) {
                unset($hotelIds[$adminId]);
            }

            unset($clientId[$client]);
            unset($this->tickClientIds[$client]);
        });

        $webSocketInstance->on('task', function ($server, $task_id, $from_id, $data) {
            $hotelId = $data['hotel'];
            $roomId = $data['roomId'];

            // 移动端推送
            $this->redisUtil = new \Org\Util\RedisHelper(null, null, 'meizhu');
            $this->redisUtil->db(C('REDIS_ORDER_PUSH_DB'))->table('newsPush');
            $newsPushResult = $this->redisUtil->hMGet($hotelId . '_' . $roomId);
            $newsPushString = $this->redisUtil->hMGet($hotelId . '_' . $roomId . ':pushNotice');
            // 使消息失效
            $this->redisUtil->myexpiry($hotelId . '_' . $roomId, 0);
            $this->redisUtil->myexpiry($hotelId . '_' . $roomId . ':pushNotice', 0);

            if (!empty($newsPushResult)){
                // 构造旧版本推送的别名
                $oldAlias = array();
                foreach ($newsPushResult as $key => $newsPushItem) {
                    array_push($oldAlias, $hotelId . '_' . $newsPushItem);
                }
                unset($newsPushItem);
                // 新版本别名
                $newAlias = $newsPushResult;
                $alias = array_merge($oldAlias, $newAlias);

                // 调用推送接口 app端
                $jpushConfig = C('JPUSH_CONFIG');
                vendor('jpush.autoload');
                $jpushUtil = new \JPush\Client($jpushConfig['appID'], $jpushConfig['appSecret'], $jpushConfig['logPath']);
                $push = $jpushUtil->push();
                $errorMsg = '';
                try {
                    $push->setPlatform(array('ios', 'android'));
                    $notification = [];
                    $push->androidNotification($newsPushString[0], $notification);
                    //推送唤醒
                    $notification['content-available']  = true;
                    $notification['sound'] = 'sound.caf';
                    $push->iosNotification($newsPushString[0], $notification);

                    // 查看是否在调试模式,如果是,消息只发送到测试tag的用户
                    if ($jpushConfig['debug']) {
                        $push->addAlias($alias);
                        $push->addTag(array('test'));
                        $push->options(array(
                            'apns_production' => false,
                        ));
                    } else {
                        $push->addAlias($alias);
                        $push->options(array(
                            'apns_production' => true,
                        ));
                    }

                    $result = $push->send();
                } catch (\JPush\Exceptions\APIConnectionException $e) {
                    $errorMsg = $e->getMessage();
                    \Think\Log::write($errorMsg . "\r\n", \Think\Log::ERR, '', $jpushConfig['logPath']);
                } 
            }

        });

        $webSocketInstance->on('request', function (swoole_http_request $request, swoole_http_response $response) {
            global $hotelIds;
            $param = $request->post;

            $hotel = $param['hotel'];
            $roomId = $param['roomId'];

            // web推送
            $HotelRoomAuthorityModel = D('HotelRoomAuthority');
            $adminHotelModel = D('AdminHotel');
            foreach ($hotelIds as $adminId => $admin) {
                foreach ($admin as $client => $hotels) {
                    if (in_array($hotel, $hotels)) {
                        // 微预定 预定时推送
                        if (isset($roomId)) {
                            // 检查用户是否是客栈拥有者
                            $adminInfo = $adminHotelModel->getInfosByAdmin($adminId, 1, $hotel);
                            $isAdminUser = empty($adminInfo) ? false : true;
                            // 管理员对该房间的权限  (掌柜直接推送)
                            $HotelRoomAuthorityInfo = $HotelRoomAuthorityModel->getInfoByUserRoom($adminId, $roomId);
                            if (!empty($HotelRoomAuthorityInfo) || $isAdminUser) {
                                $this->webSocketServer->push($client, json_encode(array(
                                    'httpstatus' => 200,
                                    'status' => 200,
                                    'type' => 1,
                                )));
                            }
                        } else {
                            // 房态刷新时推送
                            $this->webSocketServer->push($client, json_encode(array(
                                'httpstatus' => 200,
                                'status' => 200,
                                'type' => 1,
                            )));
                        }
                    }
                }
            }

            // 移动推送
            $this->webSocketServer->task($param);

            $response->end('success');
        });

        $webSocketInstance->on('finish', function ($server) {
        });

        $webSocketInstance->set(array(
            'task_worker_num' => 4,
            'worker_num'      => 1,
            'max_request'     => 5000,
            'daemonize' => 1,
        ));
        $webSocketInstance->start();
    }

    public function checkRoomPush() {
        $this->webSocketCheckRoomServer = $webSocketInstance = new \swoole_websocket_server(C('SOCKET_SERVER_CHECKROOM_IP'), C('SOCKET_SERVER_CHECKROOM_PORT'));
        $adminClient=[];
        $webSocketInstance->on('WorkerStart', function ($server, $worker_id) {
        });

        $webSocketInstance->on('message', function ($server, $frame) {
            global $adminClient;
            $msg = $frame->data;
            $msg = json_decode($msg, true);

            // 保存浏览器id用户关系
            if (empty($adminClient)) {
                $adminClient[$msg['adminId']][$frame->fd] = $frame->fd;
            } else {
                if (isset($adminClient[$msg['adminId']])) {
                    $adminClient[$msg['adminId']][$frame->fd] = $frame->fd;
                } else {
                    $adminClient[$msg['adminId']][$frame->fd] = $frame->fd;
                }
            }

            // 房态刷新时推送
            if (isset($adminClient[$msg['adminId']])) {
                foreach ($adminClient[$msg['adminId']] as $clientKey => $client) {
                    $server->push($client, json_encode(array(
                        'httpstatus' => 200,
                        'status' => 200,
                    )));
                }
            }

        });

        //监听WebSocket连接关闭事件
        $webSocketInstance->on('close', function ($server, $client, $from_id) {
            global $adminClient;
            foreach ($adminClient as $adminId => &$clients) {
                if (in_array($client,  $clients)) {
                    unset($clients[$client]);
                }

                if (empty($adminClient[$adminId])) {
                    unset($adminClient[$adminId]);
                }
            }
        });

        $webSocketInstance->on('task', function ($server, $task_id, $from_id, $data) {
            $hotelId = $data['hotel'];
            $subOrderIds = json_decode($data['subOrderIds'], true);
            $from = $data['from'];
            if ($from == 'web') {
                // web端向移动端推送
                $redisUtil = new \Org\Util\RedisHelper(null, null, 'meizhu');
                $redisUtil->db(C('REDIS_ORDER_PUSH_DB'))->table('checkRoomPush');

                foreach ($subOrderIds as $key => $subOrderId) {
                    $checkRoomData = $redisUtil->hMGet($hotelId . '_' . $subOrderId);
                    $checkRoomData = json_decode($checkRoomData[0], true);
                    if (!empty($checkRoomData)){
                        $jpushConfig = C('JPUSH_CONFIG');
                        // 构造推送别名
                        $alias = array();
                        $aliasTest = array();
                        foreach ($checkRoomData as $key => $checkRoomItem) {
                            array_push($alias, $checkRoomItem['adminlogin_id']);
                            if ($jpushConfig['debug']) {
                                array_push($aliasTest, $checkRoomItem['adminlogin_id'] . '_test');
                            }
                        }
                        unset($checkRoomItem);

                        // 调用推送接口 app端
                        vendor('jpush.autoload');
                        $jpushUtil = new \JPush\Client($jpushConfig['appID'], $jpushConfig['appSecret'], $jpushConfig['logPath']);
                        $push = $jpushUtil->push();
                        $errorMsg = '';
                        try {
                            $push->setPlatform(array('ios', 'android'));
                            $notification = [];
                            $push->androidNotification($checkRoomData[0]['notice'], $notification);
                            //推送唤醒
                            $notification['content-available']  = true;
                            $notification['sound'] = 'sound.caf';
                            $push->iosNotification($checkRoomData[0]['notice'], $notification);

                            // 查看是否在调试模式,如果是,消息只发送到测试tag的用户
                            if ($jpushConfig['debug']) {
                                $push->addAlias($aliasTest);
                                $push->addTag(array('test'));
                                $push->options(array(
                                    'apns_production' => false,
                                ));
                            } else {
                                $push->addAlias($alias);
                                $push->options(array(
                                    'apns_production' => true,
                                ));
                            }

                            $result = $push->send();
                        } catch (\JPush\Exceptions\APIConnectionException $e) {
                            $errorMsg = $e->getMessage();
                            \Think\Log::write($errorMsg . "\r\n", \Think\Log::ERR, '', $jpushConfig['logPath']);
                        } 
                    }

                    // 使消息失效
                    $redisUtil->myexpiry($hotelId . '_' . $subOrderId, 0);
                }
                unset($subOrderId);
            } else if ($from == 'app') {
                // 移动端向web端推送
                // 获取客栈掌柜用户id
                $adminHotelModel = D('AdminHotel');
                $hotelAdminInfo = $adminHotelModel->getInfosByHotel($hotelId, 1);
                $hotelAdminId = $hotelAdminInfo[0]['adminlogin_id'];
                // 发起查房用户ID
                $sendAdminId = $data['sendAdminId'];
                $adminClient = $data['adminClient'];

                if (isset($adminClient[$hotelAdminId])) {
                    foreach ($adminClient[$hotelAdminId] as $clientKey => $client) {
                        $server->push($client, json_encode(array(
                            'httpstatus' => 200,
                            'status' => 200,
                        )));
                    }
                }

                if (isset($adminClient[$sendAdminId])) {
                    foreach ($adminClient[$sendAdminId] as $clientKey => $client) {
                        $server->push($client, json_encode(array(
                            'httpstatus' => 200,
                            'status' => 200,
                        )));
                    }
                }
            }
        });

        $webSocketInstance->on('request', function (swoole_http_request $request, swoole_http_response $response) {
            global $adminClient;
            $param = $request->post;
            $param['adminClient'] = $adminClient;
            // 移动推送
            $this->webSocketCheckRoomServer->task($param);
            $response->end('success');
        });

        $webSocketInstance->on('finish', function ($server) {
        });

        $webSocketInstance->set(array(
            'task_worker_num' => 4,
            'worker_num'      => 1,
            'max_request'     => 5000,
            'daemonize' => 1,
        ));
        $webSocketInstance->start();
    }
}
